﻿using System.Collections.Generic;
using UnityEngine;

namespace Mars.GpuInstance
{
    public class GPUAnimatorInstanceCacheDataManager
    {
        private static GPUAnimatorInstanceCacheDataManager instance;
        public static GPUAnimatorInstanceCacheDataManager Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new GPUAnimatorInstanceCacheDataManager();
                    instance.AddFreeCount(maxCacheGpuAnimatorCount);
                }

                return instance;
            }
        }


        private const int maxCacheGpuAnimatorCount = 80;
        #region 初始化

        public void AddFreeCount(int gpuAnimatorCount)
        {
            for (int i = 0; i < gpuAnimatorCount; i++)
            {
                freeGPUSkinRenderBatchs.Enqueue(new GPUSkinRenderBatch());
                freeGPUMeshRenderBatchs.Enqueue(new GPUMeshRenderBatch());
                freeAnimatorInstancerRuntimeDatas.Enqueue(new GPUAnimatorInstancerRuntimeData());
                freeAnimators.Enqueue(new GPUAnimator());
            }
        }

        #endregion

        #region 释放

        public void Tick()
        {
            if (Time.frameCount % 20 == 0)
            {
                ReleaseGpuAnimatorData();
            }
        }

        private void ReleaseGpuAnimatorData()
        {
            int count = freeGPUSkinRenderBatchs.Count;
            if (count > 0)
            {
                var item = freeGPUSkinRenderBatchs.Dequeue();
                item.Dispose();
            }
            
            
            count = freeGPUMeshRenderBatchs.Count;
            if (count > 0)
            {
                var item = freeGPUMeshRenderBatchs.Dequeue();
                item.Dispose();
            }
            
            count = freeAnimatorInstancerRuntimeDatas.Count;
            if (count > 0)
            {
                var item = freeAnimatorInstancerRuntimeDatas.Dequeue();
                item.Dispose();
            }
            
            count = freeAnimators.Count;
            if (count > 0)
            {
                var item = freeAnimators.Dequeue();
            }
        }
        #endregion
        

        #region GPU Skin相关

        private readonly Queue<GPUAnimator> freeAnimators = new Queue<GPUAnimator>(100);
        private readonly Queue<GPUSkinRenderBatch> freeGPUSkinRenderBatchs = new Queue<GPUSkinRenderBatch>(100);
        private Queue<GPUMeshRenderBatch> freeGPUMeshRenderBatchs = new Queue<GPUMeshRenderBatch>(50);
        private Queue<GPUAnimatorInstancerRuntimeData> freeAnimatorInstancerRuntimeDatas = new Queue<GPUAnimatorInstancerRuntimeData>(100);
        
        public GPUAnimator GetOrCreateGPUAnimator()
        {
            if (freeAnimators.Count > 0)
            {
                var animator = freeAnimators.Dequeue();
                animator.ResetAnimator();
                return animator;
            }

            return new GPUAnimator();
        }

        public void RecycleGPUAnimator(GPUAnimator animator)
        {
            if (animator == null)
            {
                return;
            }
            freeAnimators.Enqueue(animator);
        }

        public void ClearGPUSkinData()
        {
            freeAnimators.Clear();
            
            foreach (var item in freeAnimatorInstancerRuntimeDatas)
            {
                item.Dispose();
            }
            freeAnimatorInstancerRuntimeDatas.Clear();
            
            foreach (var item in freeGPUSkinRenderBatchs)
            {
                item.Dispose();
            }
            freeGPUSkinRenderBatchs.Clear();

            foreach (var item in freeGPUMeshRenderBatchs)
            {
                item.Dispose();
            }
            freeGPUMeshRenderBatchs.Clear();
            
        }
        
        public GPUSkinRenderBatch GetOrCreateGPUSkinRenderBatch()
        {
            if (freeGPUSkinRenderBatchs.Count > 0)
            {
                var batch = freeGPUSkinRenderBatchs.Dequeue();
                return batch;
            }

            return new GPUSkinRenderBatch();
        }

        public void RecycleGPUSkinRenderBatch(GPUSkinRenderBatch batch)
        {
            if (batch == null)
            {
                return;
            }
            
            if (freeGPUSkinRenderBatchs.Count > maxCacheGpuAnimatorCount)
            {
                batch.Dispose();
                return;
            }
            
            batch.OnRecycle();
            freeGPUSkinRenderBatchs.Enqueue(batch);
        }

        public GPUMeshRenderBatch GetOrCreateGPUMeshRenderBatch()
        {
            if (freeGPUMeshRenderBatchs.Count > 0)
            {
                var batch = freeGPUMeshRenderBatchs.Dequeue(); 
                return batch;
            }

            return new GPUMeshRenderBatch();
        }

        public void RecycleGPUMeshRenderBatch(GPUMeshRenderBatch batch)
        {
            if (batch == null)
            {
                return;
            }
            
            if (freeGPUMeshRenderBatchs.Count > maxCacheGpuAnimatorCount)
            {
                batch.Dispose();
                return;
            }
            
            batch.OnRecycle();
            freeGPUMeshRenderBatchs.Enqueue(batch);
        }
        
        public GPUAnimatorInstancerRuntimeData GetOrCreateGPUAnimatorInstancerRuntimeData()
        {
            if (freeAnimatorInstancerRuntimeDatas.Count > 0)
            {
                var runtimeData = freeAnimatorInstancerRuntimeDatas.Dequeue();
                runtimeData.InitContainer();
                return runtimeData;
            }

            return new GPUAnimatorInstancerRuntimeData();
        }

        public void RecycleGPUAnimatorInstancerRuntimeData(GPUAnimatorInstancerRuntimeData runtimeData)
        {
            if (runtimeData == null)
            {
                return;
            }
            
            if (freeAnimatorInstancerRuntimeDatas.Count > maxCacheGpuAnimatorCount)
            {
                runtimeData.Dispose();
                return;
            }

            runtimeData.OnRecycle();
            freeAnimatorInstancerRuntimeDatas.Enqueue(runtimeData);
        }

        #endregion
    }
}
